from collections.abc import Sequence
from dataclasses import dataclass
from typing import Self, override

from tqdm import tqdm

from ...unmarshalers import UserStage
from ..interface import Processor
from .interface import Emotion, EmotionClassifier

__all__ = ["EmotionTriplet", "EmotionalClassifiedStage", "EmotionalClassificationProcessor"]


class EmotionTriplet(tuple[float, float, float]):
    """
    A triplet of emotions.

    This class represents a triplet of emotions. In this project, we only use three emotions: positive, neutral, and
    negative, whose frequencies are stored in the `positive`, `neutron`, and `negative` attributes, respectively. Their
    frequencies sum up to 1.
    """

    def __new__(cls, neutral: int, positive: int, negative: int) -> Self:
        total = neutral + positive + negative
        if total == 0:
            raise ValueError("Empty user")
        neutral_ratio = neutral / total
        positive_ratio = positive / total
        negative_ratio = negative / total
        return super().__new__(cls, (neutral_ratio, positive_ratio, negative_ratio))


@dataclass(frozen=True)
class EmotionalClassifiedStage(object):
    emotions: Sequence[EmotionTriplet]


class EmotionalClassificationProcessor(Processor[UserStage, EmotionalClassifiedStage]):
    """
    A processor that classifies the emotion of a sentence.

    This processor uses a classifier to classify the emotion of a sentence. It transforms a `UserStage` into an
    `EmotionalClassifiedStage`.
    """

    _classifier: EmotionClassifier

    def __init__(self, classifier: EmotionClassifier) -> None:
        self._classifier = classifier

    @override
    def process(self, stage: UserStage) -> EmotionalClassifiedStage:
        triplets = []
        for user in tqdm(stage.users, desc="Emotional classification"):
            positive = 0
            neutral = 0
            negative = 0
            for post in user.posts:
                emotion = self._classifier.classify(post)
                match emotion:
                    case Emotion.POSITIVE:
                        positive += 1
                    case Emotion.NEUTRAL:
                        neutral += 1
                    case Emotion.NEGATIVE:
                        negative += 1
            triplets.append(EmotionTriplet(positive, neutral, negative))
        return EmotionalClassifiedStage(triplets)
